package com.stock.service.impl;

import com.alibaba.excel.EasyExcel;
import com.github.pagehelper.PageHelper;
import com.google.gson.Gson;
import com.stock.DO.Stock4EvrDayDomain;
import com.stock.DO.Stock4MinuteDomain;
import com.stock.DO.StockExcelDomain;
import com.stock.DO.StockUpdownDomain;
import com.stock.entity.*;
import com.stock.enums.CodeEnum;
import com.stock.mapper.*;
import com.stock.properties.MarketProperties;
import com.stock.response.ResponseResult;
import com.stock.service.StockService;
import com.stock.util.DateTimeUtil;
import com.stock.vo.InnerMarketVO;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@AllArgsConstructor
@NoArgsConstructor
public class StockServiceImpl implements StockService {
    @Autowired
    private StockBusinessMapper stockBusinessMapper;
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    @Autowired
    private StockMarketLogPriceMapper stockMarketLogPriceMapper;
    @Autowired
    private MarketProperties marketProperties;
    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;
    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;
    @Autowired
    private StockInfoConfig stockInfoConfig;

    public StockServiceImpl(MarketProperties marketProperties,
                            StockMarketIndexInfoMapper stockMarketIndexInfoMapper,
                            StockMarketLogPriceMapper stockMarketLogPriceMapper) {
        this.marketProperties = marketProperties;
        this.stockMarketIndexInfoMapper = stockMarketIndexInfoMapper;
        this.stockMarketLogPriceMapper = stockMarketLogPriceMapper;
    }

    public StockServiceImpl(StockBusinessMapper stockBusinessMapper) {
        this.stockBusinessMapper = stockBusinessMapper;
    }


    /**
     * 功能描述：单个个股日K数据查询 ，可以根据时间区间查询数日的K线数据
     * 		默认查询历史20天的数据；
     * @param code 股票编码
     */
    @Override
    public ResponseResult<List<Stock4EvrDayDomain>> stockCreenDkLine(String stockCode) {
        //获取当前日期前推20天
        //当前时间节点
        LocalDateTime curTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //前推20
        LocalDateTime pre20Day = curTime.minusDays(20);

        //TODO 后续删除
        String avlDate="20220106142500";
        curTime=LocalDateTime.parse(avlDate, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String openDate="20220101093000";
        pre20Day=LocalDateTime.parse(openDate, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));

        List<Stock4EvrDayDomain> infos = stockRtInfoMapper.stockCreenDkLine(stockCode, pre20Day, curTime);
        return ResponseResult.ok(infos);
    }


    /**
     * 查询个股的分时行情数据，也就是统计指定股票T日每分钟的交易数据；
     *      响应数据字段：日期 交易量 交易金额 最低价 最高价 前收盘价 公司名称 开盘价 股票code 当前价
     * 功能描述：查询单个个股的分时行情数据，也就是统计指定股票T日每分钟的交易数据；
     *      如果当前日期不在有效时间内，则以最近的一个股票交易时间作为查询时间点
     * @param code 股票编码
     */
    @Override
    public ResponseResult<List<Stock4MinuteDomain>> stockScreenTimeSharing(String code) {
        //1.获取最近有效的股票交易时间
        LocalDateTime curDate = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //获取当前日期对应的开盘日期
        LocalDateTime openDate = DateTimeUtil.getOpenDate(curDate);
        //TODO 后续删除 mock-data
        String mockDate="20220106142500";
        curDate=LocalDateTime.parse(mockDate, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String openDateStr="20220106093000";
        openDate=LocalDateTime.parse(openDateStr, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));

        List<Stock4MinuteDomain> maps= stockRtInfoMapper.stockScreenTimeSharing(code,openDate,curDate);
        return ResponseResult.ok(maps);
    }


    /**
     * 功能描述：统计在当前时间下（精确到分钟），股票在各个涨跌区间的数量
     *  如果当前不在股票有效时间内，则以最近的一个有效股票交易时间作为查询时间点；
     * 查询当前时间下股票的涨跌幅度区间统计功能
     * 如果当前日期不在有效时间内，则以最近的一个股票交易时间作为查询点
     */
    @Override
    public ResponseResult<Map> stockUpDownScopeCount() {
        //1.获取当前时间下最近的一个股票交易时间 精确到秒
        LocalDateTime avlDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //TODO 后续删除 mock-data
        String  mockDate="20220106095500";
        avlDateTime = LocalDateTime.parse(mockDate, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        //2.查询
        List<Map> maps=stockRtInfoMapper.stockUpDownScopeCount(avlDateTime);
        //获取去股票涨幅区间的集合
        List<String> upDownRange = stockInfoConfig.getUpDownRange();
        //将list集合下的字符串映射成Map对象
        List<Map> orderMap = upDownRange.stream().map(key -> {
            Optional<Map> title = maps.stream().filter(map -> key.equals(map.get("title"))).findFirst();
            //判断对应的map是否存在
            Map tmp = null;
            if (title.isPresent()) {
                tmp = title.get();
            } else {
                tmp = new HashMap();
                tmp.put("title", key);
                tmp.put("count", 0);
            }
            return tmp;
        }).collect(Collectors.toList());
        //3.组装data
        HashMap<String, Object> data = new HashMap<>();
        data.put("time",avlDateTime.toString());
        data.put("infos",orderMap);
        //返回响应数据
        return ResponseResult.ok(data);
    }


    @Override
    public ResponseResult<Map> stockTradeVol4InnerMarket() {
        //1.获取最近的股票交易日时间，精确到分钟 T交易日
        LocalDateTime tDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //对应的开盘时间
        LocalDateTime tOpenTime = DateTimeUtil.getOpenDate(tDateTime);
        //TODO 后续注释掉 mock-data
        String tDateStr="20220103143000";
        tDateTime = LocalDateTime.parse(tDateStr, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        //mock-data
        String openDateStr="20220103093000";
        tOpenTime = LocalDateTime.parse(openDateStr, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));


        //获取T-1交易日
        LocalDateTime preTDateTime = DateTimeUtil.getPreviousTradingDay(tDateTime);
        LocalDateTime preTOpenTime=DateTimeUtil.getOpenDate(preTDateTime);
        //TODO 后续注释掉 mock-data
        String preTStr="20220102143000";
        preTDateTime = LocalDateTime.parse(preTStr, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        //mock-data
        String openDateStr2="20220102093000";
        preTOpenTime= LocalDateTime.parse(openDateStr2, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        //获取a股大盘ID集合
        List<String> inner = marketProperties.getInner();

        //2.获取T日的股票大盘交易量统计数据
        List<Map> tData=stockMarketIndexInfoMapper.stockTradeVolCount(stockInfoConfig.getInner(),tOpenTime,tDateTime);
        //3.获取T-1的数据
        List<Map> preTData=stockMarketIndexInfoMapper.stockTradeVolCount(stockInfoConfig.getInner(),preTOpenTime,preTDateTime);


        //4.组装数据
        HashMap<String, List<Map>> data = new HashMap<>();
        data.put("volList",tData);
        data.put("yesVolList",preTData);
        return ResponseResult.ok(data);
    }


    /**
     * 将指定页的股票数据导出到excel表下
     * @param response
     * @param page  当前页
     * @param pageSize 每页大小
     */
    @Override
    public void stockExport(HttpServletResponse response, Integer page, Integer pageSize) {
        try {
            //1.设置响应数据的类型:excel
            response.setContentType("application/vnd.ms-excel");
            //2.设置响应数据的编码格式
            response.setCharacterEncoding("utf-8");
            //3.设置默认的文件名称
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("stockRt", "UTF-8");
            //设置默认文件名称
            response.setHeader("content-disposition", "attachment;filename=" + fileName + ".xlsx");
            //4.分页查询股票数据
            PageHelper.startPage(page,pageSize);
            List<StockUpdownDomain> infos = this.stockRtInfoMapper.stockAll();
            Gson gson = new Gson();
            List<StockExcelDomain> excelDomains = infos.stream().map(info -> {
                StockExcelDomain domain=new StockExcelDomain();
                BeanUtils.copyProperties(info,domain);
                return domain;
            }).collect(Collectors.toList());
            //5.导出
            EasyExcel.write(response.getOutputStream(),StockExcelDomain.class).sheet("股票数据").doWrite(excelDomains);
        } catch (IOException e) {
            log.info("股票excel数据导出异常，当前页：{}，每页大小：{}，异常信息：{}",page,pageSize,e.getMessage());
        }
    }

    /**
     * 功能描述：沪深两市涨跌停分时行情数据查询，查询T日每分钟的涨跌停数据（T：当前股票交易日）
     * 		查询每分钟的涨停和跌停的数据的同级；
     * 		如果不在股票的交易日内，那么就统计最近的股票交易下的数据
     * 	 map:
     * 	    upList:涨停数据统计
     * 	    downList:跌停数据统计
     * @return
     */
    @Override
    public ResponseResult<Map> upDownCount() {
        //1.获取股票最近的有效交易日期,精确到秒
        //当前最近有效期
        LocalDateTime curTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //开盘日期
        LocalDateTime openTime = DateTimeUtil.getOpenDate(curTime);
        //TODO mock_data 后续数据实时获取时，注释掉
        String curTimeStr="20220106142500";
        //对应开盘日期 mock_data
        String openTimeStr="20220106092500";
        curTime = LocalDateTime.parse(curTimeStr, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        openTime = LocalDateTime.parse(openTimeStr, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        //2.统计涨停的数据 约定：1代表涨停 0：跌停
        List<Map> upCount= stockRtInfoMapper.upDownCount(curTime,openTime,1);
        //3.统计跌停的数据
        List<Map> downCount= stockRtInfoMapper.upDownCount(curTime,openTime,0);
        //4.组装数据到map
        HashMap<String, List<Map>> info = new HashMap<>();
        info.put("upList",upCount);
        info.put("downList",downCount);
        //5.响应
        return ResponseResult.ok(info);
    }

    @Override
    public List<StockBusiness> getStockBusiness() {
        return stockBusinessMapper.selectAll();
    }

    @Override
    public List<InnerMarketVO> innerIndexAll() {
        //1.获取国内大盘指数编码集合
        List<String> ids = marketProperties.getInner();
        //2.获取最近有效的股票交易时间
        LocalDateTime lastDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //mock data=模仿数据
        String mockData = "20211226105600";
        lastDateTime = LocalDateTime.parse(mockData, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        //3.查询
        //3.1 根据大盘编码和日期查询 股票大盘数据详情表
        List<StockMarketIndexInfo> marketIndexInfoList = stockMarketIndexInfoMapper.selectByIdsAndTime(ids, lastDateTime);
        // 3.2 根据大盘编码和日期查询 股票大盘 开盘价与前收盘价流水表
        LocalDate localDate = lastDateTime.toLocalDate();
        List<StockMarketLogPrice> marketLogPriceList = stockMarketLogPriceMapper.selectByIdsAndDateTime(ids, localDate);
        //4. 组装数据
        return marketIndexInfoList.stream().map((marketIndexInfo) -> {
                    InnerMarketVO vo = InnerMarketVO.builder()
                            .code(marketIndexInfo.getMarkId())
                            .name(marketIndexInfo.getMarkName())
                            .curDate(marketIndexInfo.getCurTime().toString())
                            .tradeAmt(marketIndexInfo.getTradeAccount())
                            .tradeVol(marketIndexInfo.getTradeVolume())
                            .tradePrice(marketIndexInfo.getCurrentPrice())
                            .upDown(marketIndexInfo.getUpdownRate())
                            .build();
                    marketLogPriceList.forEach((marketLogPrice) -> {
                        if (marketLogPrice.getMarketCode().equals(marketIndexInfo.getMarkId())) {
                            vo.setOpenPrice(marketLogPrice.getOpenPrice());
                            vo.setPreClosePrice(marketLogPrice.getPreClosePrice());
                        }else {
                            vo.setOpenPrice(BigDecimal.ZERO);
                            vo.setPreClosePrice(BigDecimal.ZERO);
                        }
                    });
                    return vo;
                }
        ).toList();
    }


    /**
     *需求说明: 沪深两市板块分时行情数据查询，以交易时间和交易总金额降序查询，取前10条数据
     * @return
     */
    @Override
    public ResponseResult<List<StockBlockRtInfo>> sectorAllLimit() {
        //1.调用mapper接口获取数据 TODO 优化 避免全表查询 根据时间范围查询，提高查询效率
        List<StockBlockRtInfo> infos=stockBlockRtInfoMapper.sectorAllLimit();
        //2.组装数据
        if (CollectionUtils.isEmpty(infos)) {
            return ResponseResult.error(CodeEnum.NO_RESPONSE_DATA);
        }
        return ResponseResult.ok(infos);
    }

    /**
     * 沪深两市个股涨幅分时行情数据查询，以时间顺序和涨幅查询前10条数据
     * @return
     */
    @Override
    public ResponseResult<List<StockUpdownDomain>> stockIncreaseLimit() {
        //1.直接调用mapper查询前10的数据 TODO 以时间顺序取前10
        //优化：
        //获取当前最近有效时间
        StringBuilder curDateTime = new StringBuilder(DateTimeUtil.getLastDateString4Stock(LocalDateTime.now()));
        curDateTime.insert(4, "-").insert(7, "-").insert(10, " ").insert(13, ":").insert(16, ":");

        //mock数据
        String mockStr="2021-12-27 09:47:00";

        //List<StockUpdownDomain> infos=stockRtInfoMapper.stockIncreaseLimit(curDateTime.toString());
        List<StockUpdownDomain> infos=stockRtInfoMapper.stockIncreaseLimit(mockStr);
        //2.判断是否有数据
        if (CollectionUtils.isEmpty(infos)) {
            return ResponseResult.error(CodeEnum.NO_RESPONSE_DATA);
        }
        return ResponseResult.ok(infos);
    }
}
